
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import <math.h>
#import "texture2d.h"
#import "PaintingView.h"


static GLfloat triangleVertices[] = {
    160.0, 280.0, 0.0,
    160.0-90.0, 100.0, 0.0,
    160.0+90.0, 100.0, 0.0,
};


////////////////////////////////////////
//  PaintingView private methods
////////////////////////////////////////
// A class extension to declare private methods
@interface PaintingView (OpenGLConfig)
-(BOOL) createFramebuffer;
-(void) destroyFramebuffer;
-(void) configOpenGl;
-(void) createImageTexture:(UIImage *)image;

@end

////////////////////////////////////////
// implementation paintingView private
////////////////////////////////////////
@implementation PaintingView(OpenGLConfig)


////////////////////////////////////////
// Implementation private methods
////////////////////////////////////////
-(BOOL) createFramebuffer {

  /**
   * Generate IDs for a framebuffer object and a color renderbuffer
   */
  glGenFramebuffersOES(1, &viewFramebuffer);
  glGenRenderbuffersOES(1, &viewRenderbuffer);

  /** 
   * bind IDs to frame and render buffer
   */
  glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
  glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

  /**
   * This call associates the storage for the current render 
   * buffer with the EAGLDrawable (our CAEAGLLayer)
   * allowing us to draw into a buffer that will later be rendered 
   * to screen wherever the layer is (which corresponds with our view).
   */
  [context renderbufferStorage:GL_RENDERBUFFER_OES 
                  fromDrawable:(id<EAGLDrawable>)self.layer];
  glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, 
                               GL_COLOR_ATTACHMENT0_OES, 
                               GL_RENDERBUFFER_OES, 
                               viewRenderbuffer);


  /**
   * get render buffer properties
   */  
  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 
                                  GL_RENDERBUFFER_WIDTH_OES, 
                                  &backingWidth);
  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 
                                  GL_RENDERBUFFER_HEIGHT_OES, 
                                  &backingHeight);
  
  /**
   * For this sample, we also need a depth buffer, so we'll 
   * create and attach one via another renderbuffer.
   */
  glGenRenderbuffersOES(1, &depthRenderbuffer);
  glBindRenderbufferOES(GL_RENDERBUFFER_OES, 
                        depthRenderbuffer);

  glRenderbufferStorageOES(GL_RENDERBUFFER_OES, 
                           GL_DEPTH_COMPONENT16_OES, 
                           backingWidth, 
                           backingHeight);

  glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, 
                               GL_DEPTH_ATTACHMENT_OES, 
                               GL_RENDERBUFFER_OES, 
                               depthRenderbuffer);
  
  if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
     NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
     return NO;
  }
  
  return YES;
}

////////////////////////////////////////
// Clean up any buffers we have allocated.
-(void) destroyFramebuffer {
   glDeleteFramebuffersOES(1, &viewFramebuffer);
   viewFramebuffer = 0;
   glDeleteRenderbuffersOES(1, &viewRenderbuffer);
   viewRenderbuffer = 0;
   
   if( depthRenderbuffer ) {
      glDeleteRenderbuffersOES(1, &depthRenderbuffer);
      depthRenderbuffer = 0;
   }
}

////////////////////////////////////////
-(void) configOpenGl {
   /**
    * Config OpenGL pipeline 
    */

   /* projection matrix */
   glMatrixMode(GL_PROJECTION);
   CGRect frame = [self bounds];
   CGFloat scale = [self contentScaleFactor];

   /**
    * program projection matrix
    */
   glOrthof( 0, frame.size.width * scale,  /* left, right:  0, frame width */
             0, frame.size.height * scale, /* buttom, top:  0, frame height */
            -1, 1);                        /* front, back: -1, 1 */

   NSLog(@"---------> %0.2f, %0.2f", frame.size.width, frame.size.height);

   /* Setup the view port in Pixels */
   glViewport(0, 0,  
              frame.size.width * scale, frame.size.height * scale);

   /**
    * program  model & view matrix
    */
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   /**
    * config backend the pipeline
    */
   glDisable(GL_DITHER);
   glEnable(GL_TEXTURE_2D);
   glEnableClientState(GL_VERTEX_ARRAY);
   
   //glEnable(GL_BLEND);

   /**
    * Set a blending function appropriate 
    * for pre-multiplied alpha pixel data
    */
   //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
   
   //glEnable(GL_POINT_SPRITE_OES);
   //glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);

   //glPointSize(width / (kBrushScale*2));
   //glLineWidth(imageWidth / (kBrushScale*2));
}

////////////////////////////////////////
-(void) createImageTexture:(UIImage *)image {

   /**
    * Create a texture from an image
    * Get the Core Graphics image ref.
    */
   CGImageRef imageRef = [image CGImage];

   if( imageRef==nil ) {
     NSLog(@"--------->Warning imageRef is null");
     return;
   }
 
   /**
    * Get the width and height of the image
    */

   imageWidth = CGImageGetWidth(imageRef);
   imageHeight = CGImageGetHeight(imageRef);

   NSLog(@"--------> %d, %d", imageWidth, imageHeight);


   /**
    * Texture dimensions must be a power of 2. 
    * If you write an application that allows 
    * users to supply an image, you'll want to add code 
    * that checks the dimensions and takes appropriate action 
    * if they are not a power of 2.
    * kCGImageAlphaNone = RGB,
    * kCGImageAlphaPremultipliedLast=RGBA
    */
   
   /**
    * Allocate  memory needed for the bitmap context
    */
   GLubyte *imageData = (GLubyte *) calloc(imageWidth * imageHeight * 4, sizeof(GLubyte));

   /**
    * Use the bitmatp creation function provided by the Core Graphics framework. 
    * create a CG contextRef bitmap with the follwing attributes
    * CGContextRef CGBitmapContextCreate(
    *    void *data, size_t width, size_t height, size_t bitsPerComponent,
    *    size_t bytesPerRow, CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo)
    */
   CGContextRef brushContextRef;
   brushContextRef = CGBitmapContextCreate(imageData, 
                                           imageWidth, 
                                           imageHeight, 
                                           8, 
                                           imageWidth * 4, 
                                           CGImageGetColorSpace(imageRef), 
                                           kCGImageAlphaPremultipliedLast);


   /**
    * copy image to imageData in the imageRef
    * After you create the context, you can draw the image to the context.
    * draw the image in the imageRef to brushContex(imageData)
    */
   CGRect rectangle = CGRectMake(0.0f, 0.0f, (CGFloat) imageWidth, (CGFloat) imageHeight);
   CGContextDrawImage(brushContextRef, rectangle, imageRef);

  
   /**
    * You got the image in the buffer (imageData).
    * You don't need the context at this point, 
    * so you need to release it to avoid memory leaks.
    */
   CGContextRelease(brushContextRef);


   /**
    * Use OpenGL ES to generate ID for the texture.
    */
   glGenTextures(1, &textureId);

   /**
    * Bind the texture Id
    */
   glBindTexture(GL_TEXTURE_2D, textureId);

   /**
    * Set the texture parameters to use a minifying 
    * filter and a linear filer (weighted average)
    */
   glTexParameteri(GL_TEXTURE_2D, 
                   GL_TEXTURE_MIN_FILTER, 
                   GL_LINEAR);

   /**
    * Specify a 2D texture image, providing 
    * the a pointer to the image data in memory.
    * bind the imageData with GLTEXTURE_ID.
    */
   glTexImage2D(GL_TEXTURE_2D, 
                0, 
                GL_RGBA, 
                imageWidth, 
                imageHeight, 
                0, 
                GL_RGBA, 
                GL_UNSIGNED_BYTE, 
                imageData);

   /**
    * Release  the image data; it's no longer needed.
    */
   free(imageData);

}


@end




////////////////////////////////////////
// Interface for paintingView private 
// help methods
////////////////////////////////////////
@interface PaintingView(help) 
-(CGRect) findRectAroundPoint:(CGPoint) point;
-(int) findMovingAngle:(CGPoint) p;
@end


////////////////////////////////////////
@implementation PaintingView(help)

///////////////////////////////
// find the neighborhood around
// a point.
///////////////////////////////
-(CGRect) findRectAroundPoint:(CGPoint) p {
  return CGRectMake(p.x-5.0f, p.y-5.0f,
                    10.0f, 10.0f);
}

-(int) findMovingAngle:(CGPoint) p {
  CGRect touchedArea = [self findRectAroundPoint: p];

  int i;
  for(i=0; i<9; i+=3) {
      CGRect angleArea = [self findRectAroundPoint: CGPointMake(triangleVertices[i], 
                                                                triangleVertices[i+1])];
      /**
       * if 2 areas are intersects, return
       */
      //if( YES==CGRectIntersectsRect(touchedArea, angleArea) ) {
      if( YES==CGRectContainsPoint(angleArea, p) ) {
	      return i;
      }
  }
  
  NSLog(@"------------> NO intersection");

  return -1;
}

@end




////////////////////////////////////////
// implementation paintingView
// PaintingView is a view (subclass of view).
// Customizing the view:
//    1) Enable OpenGL to view through layer class
//    2) Config OpenGL pipeline
//    3) Implement the layoutSubviews
//    4) Implement handler for touch event:
//       touchesBegan, touchesMoved, touchesEnded
//    5) Implement method variables
////////////////////////////////////////
@implementation PaintingView

@synthesize  location=m_Location;
@synthesize  prevLocation=m_PrevLocation;
@synthesize  movingAngle=m_MovingAngle;

// Implement this to override the default layer class (which is [CALayer class]).
// We do this so that our view will be backed by a 
// layer that is capable of OpenGL ES rendering.
////////////////////////////////////////
+(Class) layerClass {
   return [CAEAGLLayer class];
}


////////////////////////////////////////
// The GL view is stored in the nib file. 
// When it's unarchived it's sent -initWithCoder:
// View + OpenGL EAGLLayer  
////////////////////////////////////////
-(id) initWithCoder:(NSCoder*)coder {
  /**
   * super initWithCoder will call layerClass.
   */
   if( (self = [super initWithCoder:coder])==nil ) {
      return nil;
   }

   NSLog(@"-----> initWithCoder");    

   /** 
    * set core animation properties
    *
    */
   CAEAGLLayer *eaglLayer = (CAEAGLLayer *) [self layer];

   [eaglLayer setOpaque:YES];

   /**
    * In this application, we want to retain 
    * the EAGLDrawable contents after a call to 
    * presentRenderbuffer.
    */
   [eaglLayer setDrawableProperties:[NSDictionary dictionaryWithObjectsAndKeys:
                                                     [NSNumber numberWithBool:YES], 
                                                     kEAGLDrawablePropertyRetainedBacking, 
                                                     kEAGLColorFormatRGBA8, 
                                                     kEAGLDrawablePropertyColorFormat, 
                                                     nil]];
   

   /**
    * Allocate an Opengl ES1 context
    * Set the context to the current context.
    */
   context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

   if( !context || ![EAGLContext setCurrentContext:context] ) {
      [self release];
      return nil;
   }

   /**
    * Create a texture from an image
    */
   //UIImage *image = [UIImage imageNamed:@"Green.png"];
   UIImage *image = [UIImage imageWithContentsOfFile:@"/Users/briankong/Images/blackboard-green-256x256.png"];
   //[self createImageTexture:image];
 
   /**
    * config openGL pipeline.
    */
   [self configOpenGl];
   

   /**
    * Init font and triangle
    */
   m_Text = [[Texture2D alloc] initWithString: @"A"
    				                    dimensions: CGSizeMake(20, 20)
    				                     alignment: UITextAlignmentLeft
    				                      fontName: @"Helvetica"
    				                      fontSize: 12.0f]; 

   m_Triangle = [[Triangle alloc] init];
   [m_Triangle setDelegate: self];

   // Set the view's scale factor
   [self setContentScaleFactor:1.0];

   // Make sure to start with a cleared buffer
   needsUpdate = YES;

   return self;
}


////////////////////////////////////////
// If our view is resized, we'll be asked to layout subviews.
// This is the perfect opportunity to also update the framebuffer so that it is
// the same size as our display area.
// @Override: layoutSubviews defaut implementation does nothing.
//            it should not be called directly, call the setNeedsLayout
//            method.
-(void) layoutSubviews {

  NSLog(@"-----> layoutSubview");
  [EAGLContext setCurrentContext: context];
  [self destroyFramebuffer];
  [self createFramebuffer];
  

  /**
   * TODO:
   * Clear the framebuffer the first time it is allocated
   */
  if( needsUpdate ) {
     [self render];
     needsUpdate = NO;
  }
}


////////////////////////////////////////
//  Triangle data source delegate
//     -(GLint) shapeStartingIndex
//     -(GLsizei) shapeNumberOfIndicesToRender
//     -(GLfloat *) shapeVertices
////////////////////////////////////////
-(GLint) shapeStartingIndex {
  //DOTO: should be variable
  return 0;
}

-(GLsizei) shapeNumberOfIndicesToRender {
  return 3;
}

-(GLfloat *) shapeVertices {
  return triangleVertices;
}

////////////////////////////////////////
// Releases resources when they are not longer needed.
-(void) dealloc {
  if( textureId ) {
     glDeleteTextures(1, &textureId);
     textureId = 0;
  }
  
  if( [EAGLContext currentContext] == context ) {
     [EAGLContext setCurrentContext:nil];
  }
  
  [context release];
  [super dealloc];
}


////////////////////////////////////////
// Handles the start of a touch
// 1) find the touched view object
// 2) convert Y coordinate from xxx to yyy
// 3) find the moving vectex
// debug
static int cnt=0;
-(void) touchesBegan:(NSSet *) touches 
           withEvent:(UIEvent *) event {

  /**
   * Get the touched object which the touched view.
   */
  UITouch* touch = [[event touchesForView:self] anyObject];
  m_PrevLocation = [touch locationInView: self];
  
  /**
   * Calculate the correct y coordinate of the touch
   */
  CGRect bounds = [self bounds];
  m_PrevLocation.y = bounds.size.height - m_PrevLocation.y;
  
  m_FirstTouch = YES;
  
  m_MovingAngle = [self findMovingAngle: m_PrevLocation];
  //NSLog(@"----->moving Angle %d", movingAngle);

}


////////////////////////////////////////
// Handles the continuation of a touch.
////////////////////////////////////////
-(void) touchesMoved:(NSSet *)touches 
           withEvent:(UIEvent *)event {  
  
  if( m_MovingAngle<0 ) { return; }

  UITouch* touch = [[event touchesForView:self] anyObject];
  
  /**
   * Convert touch point from UIView referential to 
   * OpenGL one (upside-down flip)
   */

  CGRect bounds = [self bounds];
  if( m_FirstTouch ) {
     m_FirstTouch = NO;

     /**
      * Get the previous touch location.
      */
     /*
     m_PrevLocation = [touch previousLocationInView:self];
     m_PrevLocation.y = bounds.size.height - m_PrevLocation.y;
     NSLog(@"First touch %d ------> %0.2f,%0.2f", cnt++, m_PrevLocation.x,
                                             m_PrevLocation.y);
                                             */
  } 
  else {

     /**
      * Get the current touch location.
      */
     m_Location = [touch locationInView:self];
     m_Location.y = bounds.size.height - m_Location.y;

     /*
     m_PrevLocation = [touch previousLocationInView:self];
     m_PrevLocation.y = bounds.size.height - m_PrevLocation.y;
     */


     /**
      * Make the point coordinates integers
      */
     triangleVertices[self.movingAngle] = floor(m_Location.x);
     triangleVertices[self.movingAngle+1] = floor(m_Location.y);
     [self render];
  }

  return;
}

////////////////////////////////////////
// Handles the end of a touch event when the touch is a tap.
-(void) touchesEnded:(NSSet *)touches 
           withEvent:(UIEvent *)event {

   UITouch*  touch = [[event touchesForView:self] anyObject];

    //NSLog(@"%d ------> touchesEnded", cnt);
   m_Location = [touch locationInView: self];
   CGRect bounds = [self bounds];
   m_Location.y = bounds.size.height - m_Location.y;

   triangleVertices[self.movingAngle] = floor(m_Location.x);
   triangleVertices[self.movingAngle+1] = floor(m_Location.y);
   [self render];
}

////////////////////////////////////////
// Handles the end of a touch event.
-(void) touchesCancelled:(NSSet *)touches 
               withEvent:(UIEvent *)event {
  // If appropriate, add code necessary to save the state of the application.
  // This application is not saving state.
  //NSLog(@"------> touchesCancelled");
}

////////////////////////////////////////
-(void) setBrushColorWithRed:(CGFloat)red 
                       green:(CGFloat)green 
                        blue:(CGFloat)blue {

  // Set the brush color using premultiplied alpha values
  glColor4f(red * kBrushOpacity,
            green * kBrushOpacity,
            blue * kBrushOpacity,
            kBrushOpacity);
}


////////////////////////////////////////
// update the screen
-(void) render {

   [EAGLContext setCurrentContext:context];
   
   /** 
    * Config OpenGL pipe state
    */
   glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

   /* clear background */
   glClearColor(1.0, 1.0, 1.0, 0.0);
   
   /* clear color buffer */
   glClear(GL_COLOR_BUFFER_BIT);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();


   //glTranslatef(100.0f, 200.0f, 0.0f);
   //glScalef(1.0f, 1.0f, 1.0f);
   glColor4f(0.0, 0.0, 1.0, 1.0);

   glLineWidth(1.0f);
   
   glVertexPointer(3, GL_FLOAT, 0, triangleVertices);

   /**
    * Enable OpenGL Client.
    * Draw array vertices.
    * Disable OpenGl Client.
    */
   glEnableClientState(GL_VERTEX_ARRAY);   

   
   [m_Triangle drawInRect: CGRectMake(0, 0, 10, 10)];
   //glDrawArrays(GL_LINE_LOOP, 0, 3);
   glDisableClientState(GL_VERTEX_ARRAY);


   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glColor4f(0.0, 0.0, 0.0, 1.0);

   /**
    * Enable OpenGL client
    * Draw text
    * Disable OpenGL client
    */
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   
      [m_Text drawInRect: CGRectMake(triangleVertices[0], triangleVertices[1], 20, 20)];
  
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   glDisableClientState(GL_VERTEX_ARRAY);
  
   glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 
   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);


   /**
   * Display the buffer
   */
   glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
   [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}


@end

/*
+(UIImage *)imageFromText:(NSString *)text
{
  UIFont *font = [UIFont systemFontOfSize:20.0];  
  CGSize size  = [text sizeWithFont:font];

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef contextRef =  CGBitmapContextCreate (NULL,
                                                    size.width, size.height,
                                                    8, 4*size.width,
                                                    colorSpace,
                                                    kCGImageAlphaPremultipiedLast
                                                    );
  CGColorSpaceRelease(colorSpace);
  UIGraphicsPushContext(contextRef);

  [text drawAtPoint:CGPointMake(0.0, 0.0) withFont:font];
  //UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIImage *image = [UIImage imageWithCGImage:CGBitmapContextCreateImage(contextRef)];

  UIGraphicsPopContext();

  return image;
}
 */
